package com.woniu.commonswitch.aop;

import com.woniu.commonswitch.annotation.ServiceSwitch;
import com.woniu.commonswitch.constant.Constant;
import com.woniu.commonswitch.exception.BusinessException;
import com.woniu.commonswitch.util.Result;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * <p>
 * 教你一招 SpringBoot + 自定义注解 + AOP 打造通用开关
 * 程序员蜗牛
 * </p>
 */
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class ServiceSwitchAOP {

    private final StringRedisTemplate redisTemplate;

    /**
     * 定义切点，使用了@ServiceSwitch注解的类或方法都拦截
     */
    @Pointcut("@annotation(com.woniu.commonswitch.annotation.ServiceSwitch)")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param point
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) {

        // 获取被代理的方法的参数
        Object[] args = point.getArgs();
        // 获取被代理的对象
        Object target = point.getTarget();
        // 获取通知签名
        MethodSignature signature = (MethodSignature) point.getSignature();

        try {

            // 获取被代理的方法
            Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
            // 获取方法上的注解
            ServiceSwitch annotation = method.getAnnotation(ServiceSwitch.class);

            // 核心业务逻辑
            if (annotation != null) {

                String switchKey = annotation.switchKey();
                String message = annotation.message();

				/*
				  获取配置项说明
				  这里有两种方式：1、配置加在Redis，查询时从Redis获取；
				  				  2、配置加在数据库，查询时从表获取。(MySQL单表查询其实很快，配置表其实也没多少数据)
				  我在工作中的做法：   直接放到数据字典里 同时加上缓存提高查询性能。
				 */

                // 这里我直接从redis中取，使用中大家可以按照意愿自行修改。
                String configVal = redisTemplate.opsForValue().get(switchKey);
                if (Constant.SWITCH_CLOSE.equals(configVal)) {
                    // 开关关闭，则返回提示。
                    return new Result<>(HttpStatus.FORBIDDEN.value(), message);
                }
            }

            // 放行
            return point.proceed(args);

        } catch (Throwable e) {
            throw new BusinessException(e.getMessage(), e);
        }
    }
}
